package com.xinghanlaser.applet.base.auth.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinghanlaser.applet.base.auth.entity.ResourceDO;
import com.xinghanlaser.applet.base.auth.entity.RoleDO;
import com.xinghanlaser.applet.base.auth.entity.RoleResourceDO;
import com.xinghanlaser.applet.base.auth.mapper.ResourceMapper;
import com.xinghanlaser.applet.base.auth.query.ResourceQuery;
import com.xinghanlaser.applet.base.auth.service.IResourceService;
import com.xinghanlaser.applet.base.auth.service.IRoleResourceService;
import com.xinghanlaser.applet.base.auth.vo.ResourcePostVO;
import com.xinghanlaser.applet.base.auth.vo.ResourcePutVO;
import com.xinghanlaser.applet.base.auth.vo.ResourceTreeVO;
import com.xinghanlaser.applet.base.auth.vo.ResourceVO;
import com.xinghanlaser.applet.base.common.entity.ResponseEntity;
import com.xinghanlaser.applet.base.common.exception.ServiceException;
import com.xinghanlaser.applet.base.common.utils.QueryUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.sql.Wrapper;
import java.util.*;
import java.util.stream.Collectors;

import static com.xinghanlaser.applet.base.auth.constant.AdminConstant.DEFECT_PID_INT;


/**
 * <p>
 * 资源表（菜单、按钮、接口） 服务实现类
 * </p>
 *
 * @author dym
 * @since 2019-11-19
 */
@Service
public class ResourceServiceImpl extends ServiceImpl<ResourceMapper, ResourceDO> implements IResourceService {
    @Resource
    private ResourceMapper resourceMapper;

    @Resource
    private IRoleResourceService roleResourceService;

    @Override
    public Set<Long> getExistResourceList(Collection<Long> ids) {
        Map<Long, ResourceDO> resourceMap = this.list(Wrappers.lambdaQuery(new ResourceDO()).select(ResourceDO::getId))
                .stream()
                .collect(Collectors.toMap(ResourceDO::getId, item -> item));

        if(resourceMap.isEmpty()) return new HashSet<>();

        return ids.stream()
                .filter(resourceMap::containsKey)
                .collect(Collectors.toSet());
    }

    @Override
    public ResponseEntity<ResponseEntity.PageResult<ResourceTreeVO>> pageByCondition(ResourceQuery query) {
        ResponseEntity.PageResult<ResourceTreeVO> result = QueryUtil.query(
                query,

                () -> new ArrayList<>(resourceMapper.selectTreePage(query.getPid(), query.getResourceName()))
        );

        return ResponseEntity.ok(result);
    }

    @Override
    public List<ResourceTreeVO> tree(Long pid) {
        return resourceMapper.selectTree(pid);
    }

    @Override
    public ResponseEntity<Set<ResourceVO>> getResourceListByRoleId(Long id) {
        Set<ResourceVO> result = resourceMapper.selectResourceByRoleId(id)
                .stream()
                .map(item -> BeanUtil.copyProperties(item, ResourceVO.class))
                .collect(Collectors.toSet());

        return ResponseEntity.ok(result);
    }

    @Override
    @DSTransactional
    public ResponseEntity<Void> removeResource(Long id) {
        validatedResourceId(id);

        // 删除资源
        this.removeByIds(batchDeleteIds(id));

        // 删除资源与角色的关联
        roleResourceService.remove(Wrappers.lambdaQuery(new RoleResourceDO()).eq(RoleResourceDO::getResourceId, id));

        return ResponseEntity.okMsg("删除资源成功");
    }

    @Override
    public ResponseEntity<Void> addResource(ResourcePostVO postVO) {
        // 校验资源代码是否重复
        if(StrUtil.isNotBlank(postVO.getResourceCode())){
            validateRoleCode(postVO.getResourceCode());
        }

        // 校验父资源是否存在
        final Long pid = postVO.getPid();
        if(pid != null && !Objects.equals(pid, DEFECT_PID_INT)){
            validatedResourceId(pid);
        }

        // 存储资源信息
        ResourceDO entity = BeanUtil.copyProperties(postVO, ResourceDO.class);
        this.save(entity);

        return ResponseEntity.okMsg("新增资源成功");
    }

    @Override
    public ResponseEntity<Void> modResource(ResourcePutVO putVO) {
        // 校验资源是否存在
        validatedResourceId(putVO.getId());

        // 校验父资源是否存在
        final Long pid = putVO.getPid();
        if(pid!= null && !Objects.equals(pid, DEFECT_PID_INT)){
            validatedResourceId(pid);
        }

        // 更新资源信息
        ResourceDO entity = BeanUtil.copyProperties(putVO, ResourceDO.class);
        this.updateById(entity);

        return ResponseEntity.okMsg("更新资源成功");
    }

    private Set<Long> batchDeleteIds(Long id){
        Set<Long> resourceIds = new HashSet<>();
        if (id != 0) {
            resourceIds.add(id);
        }

        extractResourceId(resourceMapper.selectTree(id), resourceIds);
        return resourceIds;
    }

    private void extractResourceId(List<ResourceTreeVO> resourceTreeVOS, Set<Long> resourceIds) {
        for (ResourceTreeVO resourceTreeVO : resourceTreeVOS) {
            resourceIds.add(resourceTreeVO.getId());
            extractResourceId(resourceTreeVO.getChildren(), resourceIds);
        }
    }

    private void validatedResourceId(Long resourceID){
        Optional.ofNullable(this.getById(resourceID))
                .orElseThrow(() -> new ServiceException("资源ID不存在"));
    }

    private void validateRoleCode(String resourceCode){
        if(StrUtil.isBlank(resourceCode)){
            throw new ServiceException("校验资源代码不能为空");
        }

        ResourceDO exist = this.getOne(Wrappers.lambdaQuery(new ResourceDO())
                .select(ResourceDO::getId)
                .eq(ResourceDO::getResourceCode, resourceCode)
                .last("LIMIT 1")
        );

        if(exist != null){
            throw new ServiceException("资源代码已存在");
        }
    }

}
